package org.example.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.example.Manager.ProductManager;
import org.example.Manager.ProductOrderManager;
import org.example.component.PayFactory;
import org.example.config.RabbitCloseOrderMQConfig;
import org.example.config.RabbitUpdateOrderMQConfig;
import org.example.constant.TimeConstant;
import org.example.controller.request.ConfirmOrderRequest;
import org.example.controller.request.ProductOrderPageRequest;
import org.example.enums.*;
import org.example.exception.BizException;
import org.example.interceptor.LoginInterceptor;
import org.example.model.EventMessage;
import org.example.model.LoginUser;
import org.example.model.ProductDO;
import org.example.model.ProductOrderDO;
import org.example.service.ProductOrderService;
import org.example.util.CommonUtil;
import org.example.util.JsonData;
import org.example.util.JsonUtil;
import org.example.vo.PayInfoVO;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 谢超
 * @since 2024-03-22
 */
@Service
@Slf4j
public class ProductOrderServiceImpl implements ProductOrderService {

    @Resource
    private ProductOrderManager productOrderManager;

    @Resource
    private ProductManager productManager;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private RabbitCloseOrderMQConfig rabbitCloseOrderMQConfig;

    @Resource
    private RabbitUpdateOrderMQConfig rabbitUpdateOrderMQConfig;

    @Resource
    private PayFactory payFactory;

    @Resource
    private RedisTemplate<Object, Object> redisTemplate;

    @Override
    public Map<String, Object> page(ProductOrderPageRequest pageRequest) {
        Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
        return productOrderManager.page(pageRequest.getPage(), pageRequest.getSize(), accountNo, pageRequest.getState());
    }

    @Override
    public String queryProductOrderState(String outTradeNo) {
        Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccountNo(outTradeNo, accountNo);

        if (productOrderDO != null) {
            return "";
        }
        return productOrderDO.getState();
    }

    /**
     * 防重提交
     * 获取最新的流量包价格
     * 订单验价
     * 如果有优惠券或者其他抵扣，验证前端显示和后台计算价格
     * 创建订单对象保存数据库
     * 发送延迟消息-用于自动关单
     * 创建支付消息对接第三方支付
     * 回调更新订单状态 (TODO)
     * 支付成功创建流量包 (TODO)
     * @param orderRequest
     * @return
     */
    @Override
    @Transactional
    public JsonData confirmOrder(ConfirmOrderRequest orderRequest) {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();

        String orderOutTradeNo = CommonUtil.getStringNumRandom(32);

        ProductDO productDO = productManager.getDetail(orderRequest.getProductId());

        // 验证价格
        this.checkPrice(productDO, orderRequest);

        // 创建订单
        ProductOrderDO productOrderDo = this.saveProductOrder(orderRequest, loginUser, orderOutTradeNo, productDO);

        // 创建支付信息
        PayInfoVO payInfoVO = PayInfoVO.builder().accountNo(loginUser.getAccountNo())
                .outTradeNo(orderOutTradeNo)
                .clientType(orderRequest.getClinetType())
                .title(productDO.getTitle())
                .description("")
                .payFee(orderRequest.getPayAmount())
                .orderPayTimeoutMils(TimeConstant.ORDER_PAY_TIMEOUT_MILLS)
                .build();

        // 发送延迟消息，关闭订单
        EventMessage eventMessage = EventMessage.builder()
                .eventMessageType(EventMessageTypeEnum.PRODUCT_ORDER_NEW.name())
                .accountNo(loginUser.getAccountNo())
                .bizId(orderOutTradeNo)
                .build();

        rabbitTemplate.convertAndSend(rabbitCloseOrderMQConfig.getOrderEventExchange(), rabbitCloseOrderMQConfig.getOrderCloseDelayRoutingKey(), eventMessage);

        // 调用支付信息
        String codeUrl = payFactory.pay(payInfoVO);
        if (StringUtils.isNoneBlank(codeUrl)) {
            Map<String, String> resultMap = new HashMap<>(2);
            resultMap.put("code_url", codeUrl);
            resultMap.put("out_trade_no", payInfoVO.getOutTradeNo());
            return JsonData.buildSuccess(resultMap);
        }

        return JsonData.buildError(BizCodeEnum.PAY_ORDER_FAIL.name());
    }

    private ProductOrderDO saveProductOrder(ConfirmOrderRequest orderRequest, LoginUser loginUser, String orderOutTradeNo, ProductDO productDO) {
        ProductOrderDO productOrderDO = new ProductOrderDO();
        // 设置用户信息
        productOrderDO.setAccountNo(loginUser.getAccountNo());
        productOrderDO.setNickname(loginUser.getUsername());

        // 设置商品信息
        productOrderDO.setProductId(productDO.getId());
        productOrderDO.setProductTitle(productDO.getTitle());
        productOrderDO.setProductSnapshot(JsonUtil.obj2Json(productDO));

        // 设置订单信息
        productOrderDO.setBuyNum(orderRequest.getBuyNum());
        productOrderDO.setOutTradeNo(orderOutTradeNo);
        productOrderDO.setCreateTime(new Date());
        productOrderDO.setDel(0);

        // 发票信息
        productOrderDO.setBillType(BillTypeEnum.valueOf(orderRequest.getBillType()).name());
        productOrderDO.setBillHeader(orderRequest.getBillHeader());
        productOrderDO.setBillReceiverEmail(orderRequest.getBillReceiverEmail());
        productOrderDO.setBillReceiverPhone(orderRequest.getBillReceiverPhone());
        productOrderDO.setBillContent(orderRequest.getBillContent());

        // 实际支付总价
        productOrderDO.setPayAmount(orderRequest.getPayAmount());
        // 总价，没用优惠价
        productOrderDO.setTotalAmount(orderRequest.getTotalAmount());
        // 订单状态
        productOrderDO.setState(ProductOrderStateEnum.NEW.name());
        // 支付类型
        productOrderDO.setPayType(ProductOrderPayTypeEnum.valueOf(orderRequest.getPayType()).name());

        // 插入数据库
        productOrderManager.add(productOrderDO);

        return productOrderDO;
    }

    private void checkPrice(ProductDO productDO, ConfirmOrderRequest orderRequest) {
        BigDecimal bizTotal = BigDecimal.valueOf(orderRequest.getBuyNum()).multiply(productDO.getAmount());

        // 比较前端传入的总价和后端计算价格是否一致, 如果有优惠价也在这边进行计算
        if (bizTotal.compareTo(orderRequest.getPayAmount()) != 0) {
            log.error("验证价格失败", orderRequest);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_PRICE_FAIL);
        }

    }

    /**
     * 延迟消息的时间 需要比订单过期 时间长⼀点，这样就不存在查询的时候，用户还能支付成功
     * 查询订单是否存在，如果已经支付则正常结束
     * 如果订单未支付，主动调用第三方支付平台查询订单状态
     * 确认未支付，本地取消订单
     * 如果第三方平台已经支付，主动的把订单状态改成已支付，造成该原因的情况可能是支付通道回调有问题，然后触发支付后的动作，如何触发？RPC还是？
     * @param eventMessage
     */
    @Override
    public boolean closeProductOrder(EventMessage eventMessage) {
        String outTradeNo = eventMessage.getBizId();
        Long accountNo = eventMessage.getAccountNo();

        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccountNo(outTradeNo, accountNo);

        // 订单不存在
        if (productOrderDO == null) {
            log.warn("订单不存在");
            return true;
        }

        // 订单已支付
        if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.PAY.name())) {
            log.info("直接确认消息，订单已支付:{}", eventMessage);
            return true;
        }

        // 订单未支付，需要向第三方支付平台查询状态
        if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.NEW.name())) {
            // 向第三方查询支付状态
            PayInfoVO payInfoVO = new PayInfoVO();
            payInfoVO.setPayType(productOrderDO.getPayType());
            payInfoVO.setOutTradeNo(outTradeNo);
            payInfoVO.setAccountNo(accountNo);

            // TODO 查询第三方支付平台查询状态

            String payResult = "";

            if (StringUtils.isBlank(payResult)) {
                // 如果为空，则未支付成功，本地取消订单
                productOrderManager.updateOrderPayState(outTradeNo, accountNo, ProductOrderStateEnum.CANCEL.name(), ProductOrderStateEnum.NEW.name());
                log.info("未支付成功，本地取消订单：{}", eventMessage);
            } else {
                // 支付成功，主动把订单状态更新为支付
                log.warn("支付成，但是微信回调通知失败，需要排查问题:{}", eventMessage);
                productOrderManager.updateOrderPayState(outTradeNo, accountNo, ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name());

                // 发送MQ消息，触发支付成功后的逻辑 TODO

            }
        }
        log.info("========================");
        return true;
    }

    /**
     * 处理微信回调通知
     * @param name
     * @param paramsMap
     */
    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public JsonData processOrderCallBackMsg(String name, Map<String, String> paramsMap) {
        String outTradeNo = paramsMap.get("out_trade_no");
        String tradeState = paramsMap.get("trade_state");
        Long accountNo = Long.valueOf(paramsMap.get("account_no"));

        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccountNo(outTradeNo, accountNo);

        Map<String, Object> content = new HashMap<>(4);
        content.put("outTradeNo", outTradeNo);
        content.put("buyNum", productOrderDO.getBuyNum());
        content.put("accountNo", accountNo);
        // 商品快照
        content.put("product", productOrderDO.getProductSnapshot());

        EventMessage eventMessage = EventMessage.builder()
                .bizId(outTradeNo)
                .accountNo(accountNo)
                .messageId(outTradeNo)
                .content(JsonUtil.obj2Json(content))
                .eventMessageType(EventMessageTypeEnum.PRODUCT_ORDER_PAY.name())
                .build();

        if (name.equalsIgnoreCase(ProductOrderEnum.ALI_PAY.name())) {
            // 支付宝支付逻辑
        } else if (name.equalsIgnoreCase(ProductOrderEnum.WECHAT_PAY.name())) {
            if ("SUCCESS".equalsIgnoreCase(tradeState)) {
                // 如果key不存在则设置成功，返回true
                Boolean flag = redisTemplate.opsForValue().setIfAbsent(outTradeNo, "ok", 3, TimeUnit.DAYS);

                if (flag) {
                    rabbitTemplate.convertAndSend(rabbitUpdateOrderMQConfig.getOrderEventExchange(), rabbitUpdateOrderMQConfig.getOrderUpdateTrafficRoutingKey(), eventMessage);
                    return JsonData.buildSuccess();
                }
            }

        }

        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_CALLBACK_NOT_SUCCESS);
    }

    /**
     * 处理订单队列消费消息
     * @param eventMessage
     */
    @Override
    public void handleProductOrderMessage(EventMessage eventMessage) {
        String messageType = eventMessage.getEventMessageType();

        try {
            if (EventMessageTypeEnum.PRODUCT_ORDER_NEW.name().equalsIgnoreCase(messageType)) {
                // 如果队列类型为PRODUCT_ORDER_NEW，则是新建订单队列，需要走关单逻辑
                closeProductOrder(eventMessage);
            } else if (EventMessageTypeEnum.PRODUCT_ORDER_PAY.name().equalsIgnoreCase(messageType)) {
                // 如果队列类型为PRODUCT_ORDER_PAY，则是支付订单队列，需要走更新订单状态逻辑
                String outTradeNo = eventMessage.getBizId();
                Long accountNo = eventMessage.getAccountNo();
                productOrderManager.updateOrderPayState(outTradeNo, accountNo, ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name());
                log.info("订单更新成功:{}", eventMessage);
            }
        } catch (Exception e) {
            log.error("处理订单队列消费消息异常：{}", e.getMessage());
            throw new BizException(BizCodeEnum.MQ_CONSUME_EXCEPTION);
        }
    }
}
